﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using MediaDevices;
using NLog;

namespace MTPAutoBackup
{
    public partial class Form1 : Form
    {
        private HashSet<string> _exclusionSet = new HashSet<string>(); //排除的目录列表
        private List<string> _allPathList = new List<string>(); //设备中的所有文件全路径
        private List<string> _backupPathList = new List<string>(); //备份的目录列表
        private List<FileInfo> _allFileInfoList = new List<FileInfo>(); //设备中的所有文件信息
        private Config configJson; //配置信息
        private SemaphoreSlim semaphore = new SemaphoreSlim(1, 1);
        private SemaphoreSlim loadListSemaphore = new SemaphoreSlim(1, 1);
        private static readonly Logger logger = LogManager.GetCurrentClassLogger();
        private static bool isStopDownload = false;

        public Form1()
        {
            this.StartPosition = FormStartPosition.CenterScreen; //启动在屏幕中央
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            try
            {
                init();
            }
            catch (Exception ex)
            {
                string msg = $"Form1_Load发生异常：{ex.Message}{Environment.NewLine}{ex.StackTrace}";
                Console.WriteLine(msg);
                logger.Error(msg);
                // 捕获到异常，弹窗显示异常信息
                MessageBox.Show($"发生异常：{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        public void init()
        {
            configJson = ConfigUtil.GetConfigJson();
            progressBar1.Minimum = 0;
            progressBar1.Maximum = 100;
            label1.Text = "";
            label2.Text = "";
            label3.Text = "";

            // 创建一个整数类型的 HashSet
            //HashSet<string> exclusionSet = new HashSet<string>();
            // 创建一个整数类型的 List
            //List<string> allPathList = new List<string>();

            List<string> jsonExclusionList = configJson.ExclusionList;
            foreach (string str in jsonExclusionList)
            {
                string abStr = configJson.RemotePathPrefix + str;
                string fullPath = abStr.Replace('/', '\\');
                _exclusionSet.Add(fullPath);
            }

            List<string> jsonBackupPathList = configJson.BackupPathList;
            _backupPathList.AddRange(jsonBackupPathList);
            Console.WriteLine(@"备份目录数量 = " + _backupPathList.Count);
            string join = string.Join(Environment.NewLine, _backupPathList);
            Console.WriteLine(@"备份目录" + Environment.NewLine + join);

            //创建数据库和表结构
            DbUtil dbUtil = new DbUtil();
            dbUtil.CreateDBAndTable();
            dbUtil.Close();

            Debug.WriteLine("这是一条Debug日志");
            Trace.WriteLine("这是一条Trace日志");

            // logger.Debug("这是一条调试日志");
            // logger.Info("这是一条信息日志");
            // logger.Warn("这是一条警告日志");
            // logger.Error("这是一条错误日志");
            logger.Info("程序加载完成。");
        }

        private void btnStartBackup_Click(object sender, EventArgs e)
        {
            logger.Info("开始备份。");
            label2.Text = "正在获取文信息...";
            Task.Run(() =>
            {
                RunBackup();
                Console.WriteLine("RunBackup 任务在后台线程上运行。");
            });
        }

        private void btnResumeDownload_Click(object sender, EventArgs e)
        {
            logger.Info("继续下载。");
            Task.Run(() =>
            {
                RunDownload();
                Console.WriteLine("RunDownload 任务在后台线程上运行。");
            });
        }



        private void btnStopDownload_Click(object sender, EventArgs e)
        {
            Trace.WriteLine("停止下载。");
            logger.Info("停止下载。");
            isStopDownload = true;
        }


        public async Task RunBackup()
        {
            if (!loadListSemaphore.Wait(0))
            {
                // 如果无法立即获取信号量，说明已有任务在执行，直接返回
                Console.WriteLine("RunBackup 已经有任务在执行!");
                return;
            }

            try
            {
                IEnumerable<MediaDevice> deviceList = MediaDevice.GetDevices();
                if (!deviceList.Any())
                {
                    Console.WriteLine("没有连接MTP设备");
                    throw new Exception("没有连接MTP设备！");
                    return;
                }

                //devices.FirstOrDefault(d => d.Description == "deviceName");
                MediaDevice device = deviceList.First();
                Console.WriteLine(@"Description = {0}", device.Description);
                Console.WriteLine(@"deviceList.Count() = {0}", deviceList.Count());

                device.Connect();
                _allFileInfoList.Clear();

                PrintDeviceInfo(device);

                DeviceInfo deviceInfo = new DeviceInfo();
                deviceInfo.DeviceType = device.DeviceType.ToString();
                deviceInfo.SerialNumber = device.SerialNumber;
                deviceInfo.Model = device.Model;
                deviceInfo.Name = device.Model;

                DbUtil dbUtil = new DbUtil();
                dbUtil.InsertDeviceInfo(deviceInfo);
                dbUtil.Close();

                //MediaDirectoryInfo selectedDirectory = mtpDirectories.FirstOrDefault(d => d.Name == "DCIM");
                //MediaDirectoryInfo single = mtpDirectories.Single(d => d.Name == "DCIM");

                string loadInfo = LoadList(device);

                await Task.Run(() =>
                {
                    this.BeginInvoke((Action)(() =>
                    {
                        if (label2 != null)
                        {
                            label2.Text = loadInfo;
                        }
                    }));
                });
            }
            catch (Exception ex)
            {
                string msg = $"RunBackup发生异常：{ex.Message}{Environment.NewLine}{ex.StackTrace}";
                Console.WriteLine(msg);
                logger.Error(msg);
                // 捕获到异常，弹窗显示异常信息
                MessageBox.Show($"发生异常：{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            finally
            {
                // 释放信号量
                loadListSemaphore.Release();
            }
        }

        public async Task RunDownload()
        {
            if (!semaphore.Wait(0))
            {
                // 如果无法立即获取信号量，说明已有任务在执行，直接返回
                Console.WriteLine("RunDownload 已经有任务在执行!");
                return;
            }

            isStopDownload = false;

            try
            {
                MediaDevice device = GetDefaultDevice();

                DbUtil dbUtil = new DbUtil();
                DeviceInfo deviceInfo = dbUtil.QueryDeviceInfo(device.SerialNumber);
                string devicePathName = deviceInfo.PathName;

                List<FileInfo> queryLoadList = dbUtil.QueryLoadList(deviceInfo.Id);
                int total = queryLoadList.Count;
                int loadCount = 0;
                int errCount = 0;
                foreach (FileInfo info in queryLoadList)
                {
                    try
                    {
                        if (isStopDownload)
                        {
                            label2.Text = "已停止下载";
                            break;
                        }

                        if (info.IsDirectory)
                        {
                            Console.WriteLine($"跳过设备目录:{info.RemotePath}");
                            continue;
                        }

                        string targetPath = Path.Combine(configJson.LocalPath, devicePathName, info.DirectoryPath, info.Name);
                        LoadFile(device, info.RemotePath, targetPath);
                        dbUtil.UpdateDataBacked(info.Id);
                        Console.WriteLine($"已备份:{info.RemotePath} --> {targetPath}");
                        logger.Info($"已备份:{info.RemotePath} --> {targetPath}");
                        loadCount++;
                    }
                    catch (Exception ex)
                    {
                        errCount++;
                        string msg = $"下载文件发生异常：{ex.Message}{Environment.NewLine}{ex.StackTrace}";
                        Console.WriteLine(msg);
                        logger.Error(msg);
                        // 捕获到异常，弹窗显示异常信息
                        MessageBox.Show($"下载文件发生异常：{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }

                    await Task.Run(() =>
                    {
                        this.BeginInvoke((Action)(() =>
                        {
                            if (progressBar1 != null)
                            {
                                int percentage = (int)((double)loadCount / total * 100);
                                progressBar1.Value = percentage;
                                label1.Text = $"{loadCount}/{total}";
                            }

                            if (label3 != null && errCount > 0)
                            {
                                label3.Text = $"异常:{errCount}";
                            }
                        }));
                    });
                }

                dbUtil.Close();
            }
            catch (Exception ex)
            {
                string msg = $"RunDownload发生异常：{ex.Message}{Environment.NewLine}{ex.StackTrace}";
                Console.WriteLine(msg);
                logger.Error(msg);
                // 捕获到异常，弹窗显示异常信息
                MessageBox.Show($"发生异常：{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            finally
            {
                // 释放信号量
                semaphore.Release();
            }
        }

        private MediaDevice GetDefaultDevice()
        {
            IEnumerable<MediaDevice> deviceList = MediaDevice.GetDevices();
            if (!deviceList.Any())
            {
                throw new Exception("没有连接MTP设备！");
            }

            //devices.FirstOrDefault(d => d.Description == "deviceName");
            MediaDevice device = deviceList.First();
            device.Connect();
            return device;
        }

        private void PrintDeviceInfo(MediaDevice device)
        {
            string model = device.Model;
            PowerSource powerSource = device.PowerSource;
            int powerLevel = device.PowerLevel;
            string serialNumber = device.SerialNumber;
            DeviceType deviceType = device.DeviceType;

            Console.WriteLine(@"model = {0}", model);
            Console.WriteLine(@"powerSource = {0}", powerSource.ToString());
            Console.WriteLine(@"powerLevel = {0}", powerLevel);
            Console.WriteLine(@"serialNumber = {0}", serialNumber);
            Console.WriteLine(@"deviceType = {0}", deviceType.ToString());
        }


        //下载文件列表
        private string LoadList(MediaDevice device)
        {
            MediaDirectoryInfo rootDirectory = device.GetRootDirectory();
            Console.WriteLine(rootDirectory.FullName);
            IEnumerable<MediaDirectoryInfo> mtpDirectoryInfoList = rootDirectory.EnumerateDirectories();
            MediaDirectoryInfo rootPathInfo = mtpDirectoryInfoList.First();
            Console.WriteLine(rootPathInfo.FullName);

            // IEnumerable<MediaDirectoryInfo> directoryInfos = rootDirectory.EnumerateDirectories();
            // foreach (MediaDirectoryInfo info in directoryInfos)
            // {
            //     Console.WriteLine(@"{0}  {1}", info.FullName, info.Name);
            // }
            //ProcessDirectory(directoryInfo);

            Dictionary<string, MediaDirectoryInfo> pathInfoMap = GetPathInfoMap(rootPathInfo);

            Console.WriteLine(@"mtpPathToInfoMap.Count = " + pathInfoMap.Count);
            // 遍历 Dictionary
            foreach (KeyValuePair<string, MediaDirectoryInfo> pair in pathInfoMap)
            {
                MediaDirectoryInfo value = pair.Value;
                Console.WriteLine(pair.Key + @" = " + value.FullName);
                ProcessDirectory(value);
            }

            Console.WriteLine(@"文件列表加载完成! 文件个数=" + _allFileInfoList.Count);

            // 提取新数据，插入数据库
            DbUtil dbUtil = new DbUtil();

            DeviceInfo deviceInfo = dbUtil.QueryDeviceInfo(device.SerialNumber);
            string deviceId = deviceInfo.Id;

            List<FileInfo> queryAllData = dbUtil.QueryAllData(deviceId);
            HashSet<string> queryAllRemotePathSet = new HashSet<string>(queryAllData.Count);
            foreach (FileInfo info in queryAllData)
            {
                queryAllRemotePathSet.Add(info.RemotePath);
            }

            string remotePathPrefix = configJson.RemotePathPrefix;
            remotePathPrefix = remotePathPrefix.Replace('/', '\\');

            List<FileInfo> newAddFileInfoList = new List<FileInfo>();

            ulong allCount = dbUtil.QueryAllCount();
            foreach (FileInfo info in _allFileInfoList)
            {
                if (queryAllRemotePathSet.Contains(info.RemotePath))
                {
                    continue;
                }

                allCount++;
                info.Id = allCount.ToString();
                info.DeviceId = deviceId;
                info.Sort = allCount;

                if (!info.IsDirectory)
                {
                    string displaySize = FileUtils.ByteCountToDisplaySize(info.Size);
                    info.DisplaySize = displaySize;
                }

                string directoryPath = info.DirectoryPath;
                string result = directoryPath.TrimStart(remotePathPrefix.ToCharArray());
                info.DirectoryPath = result;
                //Console.WriteLine(info);

                newAddFileInfoList.Add(info);
            }

            dbUtil.InsertData(newAddFileInfoList);
            int insertCount = newAddFileInfoList.Count;
            int deleteCount = 0;

            // 对比已备份的数据，筛选出已删除的数据
            HashSet<string> allPathSet = new HashSet<string>(_allFileInfoList.Count);
            foreach (FileInfo info in _allFileInfoList)
            {
                allPathSet.Add(info.RemotePath);
            }

            foreach (FileInfo info in queryAllData)
            {
                if (allPathSet.Contains(info.RemotePath))
                {
                    continue;
                }

                deleteCount++;
                // 删除
                dbUtil.LogicalDeleteData(info.Id);
            }

            dbUtil.Close();

            string rel = $"新增{insertCount}条,删除{deleteCount}条数据";
            return rel;
        }


        private Dictionary<string, MediaDirectoryInfo> GetPathInfoMap(MediaDirectoryInfo rootPathInfo)
        {
            Dictionary<string, MediaDirectoryInfo> mtpPathToInfoMap = new Dictionary<string, MediaDirectoryInfo>();

            foreach (string str in _backupPathList)
            {
                string fullPath = str.Replace('/', '\\');
                string result = fullPath.Trim('\\');
                string[] split = result.Split('\\');
                Console.WriteLine(@"{0} = {1}", str, split.Length);

                IEnumerable<MediaDirectoryInfo> mtpDirectories = rootPathInfo.EnumerateDirectories();

                foreach (string subName in split)
                {
                    MediaDirectoryInfo single = mtpDirectories.Single(d => d.Name == subName);
                    mtpPathToInfoMap.Remove(str);
                    mtpPathToInfoMap.Add(str, single);
                    mtpDirectories = single.EnumerateDirectories();
                }
            }

            return mtpPathToInfoMap;
        }


        private void ProcessDirectory(MediaDirectoryInfo parent)
        {
            string directoryFullName = parent.FullName;
            IEnumerable<MediaFileInfo> fileInfos = parent.EnumerateFiles();
            if (fileInfos.Any())
            {
                FileInfo fileInfo = new FileInfo(parent.Name, true, directoryFullName, directoryFullName);
                _allFileInfoList.Add(fileInfo);
            }

            foreach (MediaFileInfo info in fileInfos)
            {
                if (_exclusionSet.Contains(info.FullName))
                {
                    continue;
                }

                //Console.WriteLine(@"文件 {0}  {1}", info.FullName, info.Name);
                _allPathList.Add(info.FullName);

                string name = info.Name;
                ulong length = info.Length;
                DateTime lastWriteTime = info.LastWriteTime.GetValueOrDefault();

                //string directoryPath = Path.GetDirectoryName(info.FullName);
                FileInfo fileInfo = new FileInfo(name, false, info.FullName, directoryFullName);
                fileInfo.Size = length;
                fileInfo.LastModified = lastWriteTime.ToString("yyyy-MM-dd HH:mm:ss");

                _allFileInfoList.Add(fileInfo);
            }

            IEnumerable<MediaDirectoryInfo> directoryInfos = parent.EnumerateDirectories();
            foreach (MediaDirectoryInfo info in directoryInfos)
            {
                if (_exclusionSet.Contains(info.FullName))
                {
                    continue;
                }

                //Console.WriteLine(@"目录 {0}  {1}", info.FullName, info.Name);
                ProcessDirectory(info);
            }
        }

        //下载文件
        public void LoadFile(MediaDevice device, string sourcePath, string targetPath)
        {
            //string sourcePath = Path.Combine("", "");
            //string targetPath = Path.Combine("", "");
            string directory = Path.GetDirectoryName(targetPath);
            if (!Directory.Exists(directory))
            {
                Directory.CreateDirectory(directory);
            }

            using (FileStream fileStream = File.Create(targetPath))
            {
                device.DownloadFile(sourcePath, fileStream);
                fileStream.Flush();
            }
            // FileStream fileStream = File.Create(targetPath);
            // device.DownloadFile(sourcePath, fileStream);
            // fileStream.Flush();
            // fileStream.Close();
        }
    }
}